#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stack>

using namespace std;

class ErrorEvent{
private:
    void IOError(int args);
    void MemoryError(int args);
    void NetworkError(int args);

public:
    void ThrowError(int type, int args);

    ErrorEvent();
};

class ChessEvent{
private:

public:
    void WinChess(int playerid, int args);
    void SeekPeace(int playerid, int args);
    void ConfessChess(int playerid, int args);
    void RepentStep(int playerid, int args);
};

class ChessNode {
private:
    int *Location;
    int PlayerID;
    int StepOrdinal;

public:
    int* LOCATION();
    int PLAYERID();
    int STEPORDINAL();

    void SetLocation(int *location);
    void SetLocation(int x, int y);
    void SetPlayer(int playerid);
    void SetStep(int stepordinal);
    void SetChess(int *location, int playerid, int stepordinal);
    void SetChess(int x, int y, int playerid, int stepordinal);

    ChessNode();
};

class ChessRoute {
private:
    stack<ChessNode> Route;
    int FirstPlayer;
    int NextPlayer;

public:
    bool isEmpty();
    bool isChecked();
    int StepCount();
    int showFirstPlayerID();
    int showNextPlayerID();
    ChessNode RecentStep();

    void AddStep(ChessNode Step);
    void SetPlayers(int firstplayerid, int nextplayerid);
    void DelRecentStep();
    void DelTwoRecentStep();

    ChessRoute();
};

class CheckerBoard{
private:
    int Size;
    ChessEvent chessEvent;
    ErrorEvent errorEvent;

    void ReSetCheckerBoard();

public:
    ChessRoute Route;
    ChessNode** Board;

    void ConsoleDisplay(string args);

    CheckerBoard(int size);
    CheckerBoard();
    void SetSize(int size);

    bool PlaceNode(int *location, int placeplayerid);
    bool PlaceNode(int location_x, int location_y, int placeplayerid);
    void RepentStep(int repentplayerid);
    void ConfessChess(int confessplayerid);
    void SeekPeace(int seekplayerid);
    void WinChess(int winplayerid);
};

class GoBangAlgorithm {
private:
    string ChessNodeTranslate(ChessNode a, ChessRoute r);
    char* StringToCharArr(string str);
    string SubStringMix(string left, string right);
    string StringReverse(string str);

public:
    GoBangAlgorithm();
    int GoBangWinnerScanner(ChessNode **Board, ChessNode ScanNode, int Location_X, int Location_Y, int Count, int Size, int Mode, int Step);
    string* GoBangScanner(ChessNode ScanNode, ChessNode** Board, ChessRoute r, int Size);
    bool bKMP(string StrLong, string StrShort);
    int iKMP(string StrLong, string StrShort);
    int GoBangJudger(ChessRoute r, ChessNode **Board, int Size);
};

/**
 * the Constructor of ChessNode
 */
ChessNode::ChessNode() {
    Location = new int[2];
    PlayerID = -1;
    StepOrdinal = -1;
}

/**
 * Set the ChessNode
 * @param x ChessNode's Location X
 * @param y ChessNode's Location Y
 * @param playerid ChessNode's put Player
 * @param stepordinal the Ordinal of the step count
 */
void ChessNode::SetChess(int x, int y, int playerid, int stepordinal) {
    Location[0] = x; Location[1] = y;
    PlayerID = playerid; StepOrdinal = stepordinal;
}

/**
 * Set the ChessNode
 * @param location location array of the ChessNode: (x,y)
 * @param playerid ChessNode's put Player
 * @param stepordinal the Ordinal of the step count
 */
void ChessNode::SetChess(int *location, int playerid, int stepordinal) {
    Location[0] = location[0]; Location[1] = location[1];
    PlayerID = playerid; StepOrdinal = stepordinal;
}

/**
 * Set the Location of the ChessNode
 * @param x ChessNode's Location X
 * @param y ChessNode's Location Y
 */
void ChessNode::SetLocation(int x, int y) {
    Location[0] = x; Location[1] = y;
}

/**
 * Set the Location of the ChessNode
 * @param location array of the ChessNode
 */
void ChessNode::SetLocation(int *location) {
    Location[0] = location[0]; Location[1] = location[1];
}

/**
 * Set the PlayerID of the ChessNode
 * @param playerid ChessNode's put Player
 */
void ChessNode::SetPlayer(int playerid) {
    PlayerID = playerid;
}

/**
 * Set the StepOrdinal of the ChessNode
 * @param stepordinal the Ordinal of the step count
 */
void ChessNode::SetStep(int stepordinal) {
    StepOrdinal = stepordinal;
}

/**
 * return ChessNode's Location Array
 * @return ChessNode's Location Array
 */
int * ChessNode::LOCATION() {
    int *arr = new int[2];
    arr[0] = Location[0]; arr[1] = Location[1];
    return arr;
}

/**
 * return ChessNode's PlayerID
 * @return ChessNode's PlayerID
 */
int ChessNode::PLAYERID() {
    return PlayerID;
}

/**
 * return ChessNode's StepCount
 * @return ChessNode's StepCount
 */
int ChessNode::STEPORDINAL() {
    return StepOrdinal;
}

/*********************************************/

/**
 * the Constructor of ChessRoute
 */
ChessRoute::ChessRoute() {
    FirstPlayer = -1;
    NextPlayer = -1;
}

/**
 * Set the two Player
 * @param firstplayerid the first move player's id
 * @param nextplayerid the second move player's id
 */
void ChessRoute::SetPlayers(int firstplayerid, int nextplayerid) {
    FirstPlayer = firstplayerid; NextPlayer = nextplayerid;
}

/**
 * Add Step to the Route
 * @param Step the recent step's ChessNode
 */
void ChessRoute::AddStep(ChessNode Step) {
    Route.push(Step);
}

/**
 * return the Recently ChessNode which in the Route
 * @return the Recently ChessNode which in the Route
 */
ChessNode ChessRoute::RecentStep() {
    return Route.top();
}

/**
 * use PlayerId to check the Route
 * @return if correct, return true or return false
 */
bool ChessRoute::isChecked() {
    if (isEmpty()) {
        return true;
    }
    else {
        if (StepCount() % 2 == 1)
        {
            if (RecentStep().PLAYERID() == FirstPlayer){
                return true;
            }
            return false;
        }
        else
        {
            if (RecentStep().PLAYERID() == NextPlayer) {
                return true;
            }
            return false;
        }

    }
}

/**
 * to see the Route if empty
 * @return if the Route is empty, return true or return false
 */
bool ChessRoute::isEmpty() {
    return Route.empty();
}

/**
 * return the size for the Route
 * @return the size of the Route
 */
int ChessRoute::StepCount() {
    return Route.size();
}

/**
 * to show the player id of the first player
 * @return the player id of the first player
 */
int ChessRoute::showFirstPlayerID() {
    return FirstPlayer;
}

/**
 * to show the player id of the second player
 * @return the player id of the second player
 */
int ChessRoute::showNextPlayerID() {
    return NextPlayer;
}

/**
 * Delete the recently step in Route
 */
void ChessRoute::DelRecentStep() {
    Route.pop();
}

/**
 * Delete the recently two steps in Route
 */
void ChessRoute::DelTwoRecentStep() {
    Route.pop();
    Route.pop();
}


/*********************************************/

/**
 * the Constructor of CheckerBoard
 * @param size the Size of the CheckerBoard
 */
CheckerBoard::CheckerBoard(int size) {
    Size = size;
    Board = new ChessNode*[Size+2];
    for (int i = 0; i < Size+2; i++) {
        Board[i] = new ChessNode[Size+2];
    }
    for (int i = 1; i <= Size; i++) {
        for (int j = 1; j <= Size; j++) {
            Board[i][j].SetChess(i,j,-1,-1);
        }
    }
    Route.AddStep(Board[0][0]);
    Route.AddStep(Board[0][0]);
}

/**
 * the Constructor of CheckerBoard, doing nothing
 */
CheckerBoard::CheckerBoard() {

}

/**
 * Set the size of CheckerBoard(only using non-param constructor)
 * @param size the Size of the CheckerBoard
 */
void CheckerBoard::SetSize(int size) {
    Size = size;
    ReSetCheckerBoard();
}

/**
 * a private function which using in SetSize function
 */
void CheckerBoard::ReSetCheckerBoard() {
    Board = new ChessNode*[Size+2];
    for (int i = 0; i < Size+2; i++) {
        Board[i] = new ChessNode[Size+2];
    }
    for (int i = 1; i <= Size; i++) {
        for (int j = 1; j <= Size; j++) {
            Board[i][j].SetChess(i,j,-1,-1);
        }
    }
}

/**
 * print the CheckerBoard in Console with argument
 * @param args "-ADVANCED" to show all info; "-EASY" to show latest info; default mode is "-EASY"
 */
void CheckerBoard::ConsoleDisplay(string args) {
    if (args == "-ADVANCED") {
        std::cout << "\t";
        for (int i = 1; i <= Size; i++) {
            std::cout << i << "\t";
        }
        std::cout << std::endl;

        string output = "";
        for (int i = 1; i <= Size; i++) {
            std::cout << i << "\t";
            for (int j = 1; j <= Size; j++) {
                if (Board[i][j].PLAYERID() == -1) {
                    output = " ";
                }
                else if (Board[i][j].PLAYERID() == Route.showNextPlayerID()) {
                    output = "X";
                }
                else if (Board[i][j].PLAYERID() == Route.showFirstPlayerID()) {
                    output = "O";
                }
                else {
                    output = "?";
                }
                std::cout << output << "\t";
            }
            std::cout << std::endl;
        }
    }
    else if (args == "-EASY") {
        std::cout << "the player " << Route.RecentStep().PLAYERID() << " put chess on " << "(" << Route.RecentStep().LOCATION()[0] << "," << Route.RecentStep().LOCATION()[1] << ")" << std::endl;
    }
    else {
        ConsoleDisplay("-EASY");
    }
}

bool CheckerBoard::PlaceNode(int *location, int placeplayerid) {
    Board[location[0]][location[1]].SetStep(Route.RecentStep().STEPORDINAL()+1);
    Board[location[0]][location[1]].SetPlayer(placeplayerid);
    Route.AddStep(Board[location[0]][location[1]]);
    return Route.isChecked();
}

bool CheckerBoard::PlaceNode(int location_x, int location_y, int placeplayerid) {
    Board[location_x][location_y].SetStep(Route.RecentStep().STEPORDINAL()+1);
    Board[location_x][location_y].SetPlayer(placeplayerid);
    Route.AddStep(Board[location_x][location_y]);
    return Route.isChecked();
}

void CheckerBoard::SeekPeace(int seekplayerid) {
    chessEvent.SeekPeace(seekplayerid,0);
}

void CheckerBoard::RepentStep(int repentplayerid) {
    chessEvent.RepentStep(repentplayerid,0);
}

void CheckerBoard::ConfessChess(int confessplayerid) {
    chessEvent.ConfessChess(confessplayerid,0);
}

void CheckerBoard::WinChess(int winplayerid) {
    std::cout << "玩家 " << winplayerid << " 胜利!\n";
    exit(0);
}

/*********************************************/


/**
 * the constructor of GoBangAlgorithm, doing nothing
 */
GoBangAlgorithm::GoBangAlgorithm() {
}

/**
 * a private function to reverse the string in function SubStringMix and GoBangScanner
 * @param str a string
 * @return the string which reversed from the string str
 */
string GoBangAlgorithm::StringReverse(string str) {
    string tmp = "";
    for (int i = str.length()-1; i >= 0; i--) {
        tmp += str.at(i);
    }
    return tmp;
}

/**
 * a private function to remix the two substring in function GoBangScanner
 * @param left left substring in function GoBangScanner
 * @param right right substring in function GoBangScanner
 * @return the hole string
 */
string GoBangAlgorithm::SubStringMix(string left, string right) {
    return StringReverse(left) + right;
}

/**
 * a private function to convert string to char array
 * @param str a string
 * @return the char array translated from str
 */
char * GoBangAlgorithm::StringToCharArr(string str) {
    char *arr = new char[str.length()];
    for (int i = 0; i < str.length(); i++) {
        arr[i] = str.at(i);
    }
    return arr;
}

/**
 * a private function to translate ChessNode to string in function GoBangScanner
 * @param a ChessNode which need to translate
 * @param r ChessRoute which include the Node a
 * @return a string translated from Node a
 */
string GoBangAlgorithm::ChessNodeTranslate(ChessNode a, ChessRoute r) {
    if (a.PLAYERID() == r.showFirstPlayerID()) {
        return "f";
    }
    else if (a.PLAYERID() == r.showNextPlayerID()) {
        return "n";
    }
    else {
        return "?";
    }
}

/**
 * DFS Searcher which can judge if any player has won. TIPS: don't use it!
 * @param Board ChessBoard
 * @param ScanNode the Node to search begin
 * @param Location_X the ScanNode's origin location x
 * @param Location_Y the ScanNode's origin location y
 * @param Count the count of the same Node, if Count up to five, return the playerid
 * @param Size the Size of the Board
 * @param Mode the direction of the search move
 * @param Step the step which the function has searched, if Step up to five, return zero
 * @return return winner's playerid if search succeed, or return zero
 */
/*
int GoBangAlgorithm::GoBangWinnerScanner(ChessNode **Board, ChessNode ScanNode, int Location_X, int Location_Y, int Count, int Size, int Mode, int Step) {
   int MoveVector[8][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}, {1,1}, {1,-1}, {-1,1}, {-1,-1}};
   if (Count == 5) {
       return ScanNode.PLAYERID();
   }
   if (Step >= 5) {
       return 0;
   }
   if (Board[Location_X][Location_Y].PLAYERID() == ScanNode.PLAYERID()) {
       return GoBangWinnerScaner(Board,ScanNode,Location_X+MoveVector[Mode][0],Location_Y+MoveVector[Mode][1],Count+1,Size,Mode,Step+1);
   }
   else {
       return 0;
   }
}*/

/**
 * a function to scan the latest step's nearby lines, translate them to string
 * @param ScanNode a ChessNode need to be scant
 * @param Board a ChessBoard
 * @param r a ChessRoute which include ScanNode
 * @param Size the Size of the Board
 * @return string array which translated from the ChessBoard
 */
string * GoBangAlgorithm::GoBangScanner(ChessNode ScanNode, ChessNode **Board, ChessRoute r, int Size) {
    string *arr = new string[8];
    for (int i = 0; i < 4; i++) {
        arr[i] = "";
    }
    // arr[0]: -
    for (int i = 1; i <= Size; i++) {
        arr[0] += ChessNodeTranslate(Board[ScanNode.LOCATION()[0]][i],r);
    }

    // arr[1]: |
    for (int i = 1; i <= Size; i++) {
        arr[1] += ChessNodeTranslate(Board[i][ScanNode.LOCATION()[1]],r);
    }

    // arr[2]: /
    for (int i = ScanNode.LOCATION()[0], j = ScanNode.LOCATION()[1]; i <= Size && j >= 1; i++, j--) {
        arr[2] += ChessNodeTranslate(Board[i][j],r);
    }
    string tmp2 = arr[2]; arr[2] = "";
    for (int i = ScanNode.LOCATION()[0]-1, j = ScanNode.LOCATION()[1]+1; i >= 1 && j <= Size; i--, j++) {
        arr[2] += ChessNodeTranslate(Board[i][j],r);
    }
    arr[2] = SubStringMix(tmp2, arr[2]);

    // arr[3]: "\"
    for (int i = ScanNode.LOCATION()[0], j = ScanNode.LOCATION()[1]; i >= 1 && j >= 1; i--, j--) {
        arr[3] += ChessNodeTranslate(Board[i][j],r);
    }
    string tmp3 = arr[3]; arr[3] = "";
    for (int i = ScanNode.LOCATION()[0]+1, j = ScanNode.LOCATION()[1]+1; i <= Size && j <= Size; i++, j++) {
        arr[3] += ChessNodeTranslate(Board[i][j],r);
    }
    arr[3] = SubStringMix(tmp3,arr[3]);

    for (int i = 4; i < 8; i++) {
        arr[i] = StringReverse(arr[i-4]);
    }

    return arr;
}

/**
 * the KMP algorithm to compare the StrLong and StrShrot, if StrLong's substring equals to StrShort, return true
 * @param StrLong the long string
 * @param StrShort the short string
 * @return if StrLong's substring equals to StrShort, return true, or return false
 */
bool GoBangAlgorithm::bKMP(string StrLong, string StrShort) {
    char* StrLongArr = StringToCharArr(StrLong);
    char* StrShortArr = StringToCharArr(StrShort);
    for (int i = 0; i < StrLong.length();) {
        int tmp = i;
        for (int j = 0; j < StrShort.length();) {
            if (StrLongArr[i] == StrShortArr[j]) {
                i++; j++;
                if (j == StrShort.length()) {
                    return true;
                }
            }
            else {
                i = tmp+1; j = 0;
                break;
            }
        }
    }
    return false;
}

/**
 * the KMP algorithm to compare the StrLong and StrShrot, if StrLong's substring equals to StrShort, return its count
 * @param StrLong the long string
 * @param StrShort the short string
 * @return the count of the substring matched StrShort
 */
int GoBangAlgorithm::iKMP(string StrLong, string StrShort) {
    int count = 0;
    char* StrLongArr = StringToCharArr(StrLong);
    char* StrShortArr = StringToCharArr(StrShort);
    for (int i = 0; i < StrLong.length();) {
        int tmp = i;
        for (int j = 0; j < StrShort.length();) {
            if (StrLongArr[i] == StrShortArr[j]) {
                i++; j++;
                if (j == StrShort.length()) {
                    count += 1;
                    i = tmp+1; j = 0;
                    break;
                }
            }
            else {
                i = tmp+1; j = 0;
                break;
            }
        }
    }
    return count;
}

int GoBangAlgorithm::GoBangJudger(ChessRoute r, ChessNode **Board, int Size) {
    bool isFirstPlayer = (r.RecentStep().PLAYERID() == r.showFirstPlayerID());
    string* ScanRoute = GoBangScanner(r.RecentStep(),Board,r,Size);
    if (isFirstPlayer) {
        // 判断长连禁手
        bool tmpc[4] = {false}; bool tag = false;
        for (int i = 0; i < 4; i++) {
            if (bKMP(ScanRoute[i],"ffffff")) {
                tmpc[i] = true;
                tag = true;
            }
        }
        for (int i = 0; i < 4; i++) {
            if (tmpc[i] == false) {
                if (bKMP(ScanRoute[i],"fffff")) {
                    return r.showFirstPlayerID();
                }
            }
        }
        if (tag) {
            return r.showNextPlayerID();
        }

        // 判断胜负
        for (int i = 0; i < 8; i++) {
            if (bKMP(ScanRoute[i],"fffff")) {
                return r.showFirstPlayerID();
            }
        }

        string BannedRoute3[] = {"?fff?","?ff?f?"};
        double BannedRouteWeight3[] = {0.5,1};
        string BannedRoute4[] = {"fff?f","nffff?","ffff?n","?ffff?"};
        double BannedRouteWeight4[] = {1,1,1,0.5};

        // 判断三三禁手
        double Weight3 = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 2; j++) {
                Weight3 += BannedRouteWeight3[j]*iKMP(ScanRoute[i],BannedRoute3[j]);
            }
        }
        if (Weight3 > 1.1) {
            return r.showNextPlayerID();
        }

        // 判断四四禁手
        double Weight4 = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 4; j++) {
                Weight4 += BannedRouteWeight4[j]*iKMP(ScanRoute[i],BannedRoute4[j]);
            }
        }
        if (Weight4 > 1.1) {
            return r.showNextPlayerID();
        }
    }
    else {
        for (int i = 0; i < 4; i++) {
            if (bKMP(ScanRoute[i],"nnnnn")) {
                return r.showNextPlayerID();
            }
        }
    }
    return -1;
}

void ChessEvent::RepentStep(int playerid, int args) {

}

void ChessEvent::ConfessChess(int playerid, int args) {

}

void ChessEvent::SeekPeace(int playerid, int args) {

}

ErrorEvent::ErrorEvent() {

}

int* NetworkTranslation(char* message) {
    int *arr = new int[3];
    string str[3];
    for (int i = 0, j = 0; j < 3 && i < 256; i++) {
        if(message[i] != '.' || message[i] != '-') {
            str[j] += message[i];
        }
        if (message[i] == '.' || message[i] == '-') {
            j++;
        }
    }
    for (int i = 0; i < 3; i++) {
        arr[i] = atoi(str[i].c_str());
    }
    return arr;
}

int main() {
    std::cout << "请输入棋盘大小："; int Size; std::cin >> Size;
    CheckerBoard checkerBoard(Size); GoBangAlgorithm goBangAlogrithm;
    int f, n; std::cout << "请输入先手玩家id： "; std::cin >> f; std::cout << "请输入后手玩家id： "; std::cin >> n;

    checkerBoard.Route.SetPlayers(f,n);

    checkerBoard.ConsoleDisplay("-ADVANCE");

    int sock = socket(AF_INET, SOCK_STREAM, 0);

    //服务器的ip为本地，端口号1234
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(8086);

    //向服务器发送连接请求
    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    //发送并接收数据
    char str[256];
    char recv[256];
    while (true) {
        int *arr = new int[3];
        std::cin >> str;

        write(sock, str, sizeof(str));

        arr = NetworkTranslation(str);
        checkerBoard.PlaceNode(arr[0], arr[1], arr[2]);
        checkerBoard.ConsoleDisplay("-ADVANCED");
        int tmp = goBangAlogrithm.GoBangJudger(checkerBoard.Route, checkerBoard.Board, Size);
        if (tmp != -1) {
            if (tmp == f) {
                std::cout << "先手玩家胜利\n";
                break;
            }
            if (tmp == n) {
                std::cout << "后手玩家胜利\n";
                break;
            }
        }

        read(sock, recv, sizeof(recv));

        arr = NetworkTranslation(recv);
        checkerBoard.PlaceNode(arr[0], arr[1], arr[2]);
        checkerBoard.ConsoleDisplay("-ADVANCED");
        int tmp2 = goBangAlogrithm.GoBangJudger(checkerBoard.Route, checkerBoard.Board, Size);
        if (tmp2 != -1) {
            if (tmp2 == f) {
                std::cout << "先手玩家胜利\n";
                break;
            }
            if (tmp2 == n) {
                std::cout << "后手玩家胜利\n";
                break;
            }
        }
        delete [] arr;
    }
    close(sock);
}